﻿using CommonFramework.Log;
using CommonFramework.Serialize.BytesArray;
using Net.Server;
using System;
using System.Linq;
using System.Net.Sockets;

namespace Net.Channel
{
    /// <summary>
    /// 使用Tcp实现的网络传输和接收
    /// </summary>
    public class NetTcpChannel:NetChannel
    {
        public override ProtocolType Protocol => ProtocolType.Tcp;
        public Socket Socket => Session.Socket;

        public NetTcpChannel(NetSession session) : base(session)
        {
        }

        public override void Clear()
        {
            base.Clear();
            _isConnecting = false;
        }

        #region Send

        public override void Send(BytesBuffer sendBytes)
        {
            //非法参数
            if (sendBytes == null || sendBytes.Bytes == null || sendBytes.DataLength() == 0)
            {
                LogUtil.LogError("Invalid parameter");
                return;
            }

            //正在连接状态    
            if (IsConnecting())
            {
                LogUtil.LogError("Send() failed,socket is connecting");
                return;
            }

            //未连接状态
            if (!IsConnected())
            {
                LogUtil.LogError("Send() failed,socket is disconnected");
                return;
            }

            //正常已连接状态
            {
                //进行异步发送
                BytesBuffer curSend = null;
                lock (_sendBuffer)
                {
                    _sendBuffer.Enqueue(sendBytes);
                    if (_sendBuffer.Count == 1)
                        curSend = _sendBuffer.First();
                }
                BeginSend(curSend);
            }
        }
        private void BeginSend(BytesBuffer sendBytes)
        {
            if (sendBytes != null)
            {
                Socket.BeginSend(sendBytes.Bytes, sendBytes.DataStart(), sendBytes.DataLength(),
                    SocketFlags.None, OnSendCallback, null);
            }
        }
        private void OnSendCallback(IAsyncResult result)
        {
            try
            {
                //不合法的socket状态
                if (!IsConnected())
                    return;

                int sendCount = Socket.EndSend(result);

                //累计已发送成功的字节数
                BytesBuffer curBytes = null;
                lock (_sendBuffer)
                {
                    curBytes = _sendBuffer.First();
                }
                curBytes.GetReader().AdvanceReadIndex(sendCount);

                if (curBytes.DataLength() == 0)//当前的消息包发送完,如果有则开始发送下一个消息包
                {
                    lock (_sendBuffer)
                    {
                        _sendBuffer.Dequeue();
                        curBytes = _sendBuffer.Count > 0 ? _sendBuffer.First() : null;
                    }
                }

                if (curBytes != null)//有要继续发送的消息包
                {
                    BeginSend(curBytes);
                }
            }
            catch (SocketException e)
            {
                OnSendCallbackError("Send() failed with Socket exception", e);
            }
            catch (Exception e)
            {
                OnSendCallbackError("Send() failed with exception", e);
            }
        }
        private void OnSendCallbackError(string error, Exception e = null)
        {
            LogUtil.LogError(error,e);
        }

        #endregion

        #region Receive

        public override void Receive(byte[] bytes, int offset, int length)
        {
            if (bytes == null)
                return;
            length = Math.Min(length, bytes.Length - offset);
            _recvBuffer.GetWriter().WriteBytes(bytes, offset, length);
            Receive(length, false);
        }
        public override void Receive(NetPacket packet)
        {
            BytesBuffer sendBytes = NetUtil.GetBytesFromPacket(packet);
            _recvBuffer.GetWriter().WriteBytes(sendBytes.Bytes, sendBytes.DataStart(), sendBytes.DataLength());
            Receive(sendBytes.DataLength(), false);
        }
        public void Receive(int receivedCount, bool needOffset = true)
        {
            if (receivedCount == 0)//接收到FIN信号,关闭连接
            {
                Session.NetManager.Close(Socket);
                return;
            }

            var writer = _recvBuffer.GetWriter();
            if (needOffset)
                writer.AdvanceWriteIndex(receivedCount);
            TryParseRecivedData(_recvBuffer);

            if (writer.RemainLength() < 8)
            {
                writer.TryMoveDataToOrigin();
                writer.TryExtendWithAddSize(_recvBuffer.DataLength());
            }
        }
        public void BeginReceive()
        {
            if (IsConnected())
            {
                var writer = _recvBuffer.GetWriter();
                Socket.BeginReceive(writer.Bytes(), writer.WriteIndex, writer.RemainLength(),
                    SocketFlags.None, OnReceiveCallback, null);
            }
            else
            {
                LogUtil.LogWarning("BeginReceive() failed,because IsConnected()==false");
            }
        }
        private void OnReceiveCallback(IAsyncResult result)
        {
            try
            {
                if (IsClosed())
                    return;
                int recvCount = Socket.EndReceive(result);
                Receive(recvCount);

                if (recvCount > 0)
                    BeginReceive();
            }
            catch (Exception e)
            {
                LogUtil.LogError($"Receive failed,connected={IsConnected()} ,with exception {e.GetType()} {e.Message}");

                if (IsConnected())
                    BeginReceive();
                else
                    Session.NetManager.Close(Socket);
            }
        }
        /// <summary>
        /// 根据 readBuffer中累计的字节数据解析出消息包
        /// </summary>
        private void TryParseRecivedData(BytesBuffer recvBuffer)
        {
            if (!NetUtil.TryGetPacketFromBytes(recvBuffer, out var packet))
                return;

            //解析出的消息包加到列表
            Session.NetManager.OnReceivePacket(Session,packet);
            NetEventHandler.OnReceivePacket(Session, packet);

            //继续尝试解析消息包
            if (recvBuffer.DataLength() > 2)
                TryParseRecivedData(recvBuffer);
        }

        #endregion

        #region 作为客户端Connect

        private bool _isConnecting = false;
        public override bool IsConnecting() => _isConnecting;
        public void Connect(string ip, int port)
        {
            if (string.IsNullOrEmpty(ip) || port <= 0)
            {
                LogUtil.LogError($"invalid params for Connect(),ip={ip} port={port}");
                return;
            }

            //已连接状态
            if (IsConnected())
            {
                LogUtil.LogError("socket is connected already");
                return;
            }

            //正在连接状态
            if (IsConnecting())
            {
                LogUtil.LogError("socket is connecting");
                return;
            }

            //已关闭状态
            if (IsClosed())
            {
                LogUtil.LogError("Socket shouldn't be null");
                return;
            }

            //未连接状态
            Clear();

            //开始连接
            _isConnecting = true;
            Socket.BeginConnect(ip, port, OnConnectCallback, Socket);
        }
        private void OnConnectCallback(IAsyncResult result)
        {
            try
            {
                Socket socket = result.AsyncState as Socket;
                socket.EndConnect(result);
                LogUtil.Log("Connect success");
                _isConnecting = false;

                //连接成功事件
                Session.NetManager.OnConnectSucc(this.Session);
                NetEventHandler.OnConnectSucc(this.Session);

                //开始接收数据
                BeginReceive();
            }
            catch (SocketException e)
            {
                _isConnecting = false;
                Session.NetManager.OnConnectFailed(this.Session,e.Message,e);
                NetEventHandler.OnConnectFaield(this.Session, e.Message, e);
            }
            catch (Exception e)
            {
                _isConnecting = false;
                Session.NetManager.OnConnectFailed(this.Session, e.Message, e);
                NetEventHandler.OnConnectFaield(this.Session, e.Message, e);
            }
        }

        #endregion

        #region 作为服务端Accept

        public void Accept()
        {
            Socket?.BeginAccept(OnAcceptCallback, Socket);
        }
        private void OnAcceptCallback(IAsyncResult result)
        {
            try
            {
                Socket serverSocket=result.AsyncState as Socket;
                Socket clientSocket = serverSocket.EndAccept(result);
                LogUtil.Log("Accept client " + clientSocket.RemoteEndPoint);

                NetSession clientSession = new NetSession(clientSocket,Session.NetManager,
                    (session)=>new NetTcpChannel(session));
                Session.NetManager.OnAccept(clientSession);
                NetEventHandler.OnAccept(clientSession);

                Accept();
            }
            catch (Exception e)
            {
                LogUtil.LogError("Accept client failed,with exception=" + e.Message);
                Accept();
            }
        }

        #endregion
    }
}